{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fb5c223e",
   "metadata": {},
   "source": [
    "# DAY12"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8611c59",
   "metadata": {},
   "source": [
    "今天介绍下遗传算法，在你以后的论文写作中可以水一节，胆子大的人才可以水一章 \n",
    "\n",
    "这些算法仅作为你的了解，不需要开始学习，如果以后需要在论文中用到，在针对性的了解下处理逻辑。\n",
    "\n",
    "下面介绍这几种常见的优化算法\n",
    "\n",
    "1. 遗传算法\n",
    "2. 粒子群优化\n",
    "3. 模拟退火\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d519c428",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 先运行之前预处理好的代码\n",
    "import pandas as pd\n",
    "import pandas as pd    #用于数据处理和分析，可处理表格数据。\n",
    "import numpy as np     #用于数值计算，提供了高效的数组操作。\n",
    "import matplotlib.pyplot as plt    #用于绘制各种类型的图表\n",
    "import seaborn as sns   #基于matplotlib的高级绘图库，能绘制更美观的统计图形。\n",
    " \n",
    " # 设置中文字体（解决中文显示问题）\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体\n",
    "plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号\n",
    "data = pd.read_csv('data.csv')    #读取数据\n",
    "\n",
    "# 先筛选字符串变量 \n",
    "discrete_features = data.select_dtypes(include=['object']).columns.tolist()\n",
    "# Home Ownership 标签编码\n",
    "home_ownership_mapping = {\n",
    "    'Own Home': 1,\n",
    "    'Rent': 2,\n",
    "    'Have Mortgage': 3,\n",
    "    'Home Mortgage': 4\n",
    "}\n",
    "data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)\n",
    "\n",
    "# Years in current job 标签编码\n",
    "years_in_job_mapping = {\n",
    "    '< 1 year': 1,\n",
    "    '1 year': 2,\n",
    "    '2 years': 3,\n",
    "    '3 years': 4,\n",
    "    '4 years': 5,\n",
    "    '5 years': 6,\n",
    "    '6 years': 7,\n",
    "    '7 years': 8,\n",
    "    '8 years': 9,\n",
    "    '9 years': 10,\n",
    "    '10+ years': 11\n",
    "}\n",
    "data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)\n",
    "\n",
    "# Purpose 独热编码，记得需要将bool类型转换为数值\n",
    "data = pd.get_dummies(data, columns=['Purpose'])\n",
    "data2 = pd.read_csv(\"data.csv\") # 重新读取数据，用来做列名对比\n",
    "list_final = [] # 新建一个空列表，用于存放独热编码后新增的特征名\n",
    "for i in data.columns:\n",
    "    if i not in data2.columns:\n",
    "       list_final.append(i) # 这里打印出来的就是独热编码后的特征名\n",
    "for i in list_final:\n",
    "    data[i] = data[i].astype(int) # 这里的i就是独热编码后的特征名\n",
    "\n",
    "\n",
    "\n",
    "# Term 0 - 1 映射\n",
    "term_mapping = {\n",
    "    'Short Term': 0,\n",
    "    'Long Term': 1\n",
    "}\n",
    "data['Term'] = data['Term'].map(term_mapping)\n",
    "data.rename(columns={'Term': 'Long Term'}, inplace=True) # 重命名列\n",
    "continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()  #把筛选出来的列名转换成列表\n",
    " \n",
    " # 连续特征用中位数补全\n",
    "for feature in continuous_features:     \n",
    "    mode_value = data[feature].mode()[0]            #获取该列的众数。\n",
    "    data[feature].fillna(mode_value, inplace=True)          #用众数填充该列的缺失值，inplace=True表示直接在原数据上修改。\n",
    "\n",
    "# 最开始也说了 很多调参函数自带交叉验证，甚至是必选的参数，你如果想要不交叉反而实现起来会麻烦很多\n",
    "# 所以这里我们还是只划分一次数据集\n",
    "from sklearn.model_selection import train_test_split\n",
    "X = data.drop(['Credit Default'], axis=1)  # 特征，axis=1表示按列删除\n",
    "y = data['Credit Default'] # 标签\n",
    "# 按照8:2划分训练集和测试集\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 80%训练集，20%测试集\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e43b372f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--- 1. 默认参数随机森林 (训练集 -> 测试集) ---\n",
      "训练与预测耗时: 0.9480 秒\n",
      "\n",
      "默认随机森林 在测试集上的分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.77      0.97      0.86      1059\n",
      "           1       0.79      0.30      0.43       441\n",
      "\n",
      "    accuracy                           0.77      1500\n",
      "   macro avg       0.78      0.63      0.64      1500\n",
      "weighted avg       0.77      0.77      0.73      1500\n",
      "\n",
      "默认随机森林 在测试集上的混淆矩阵：\n",
      "[[1023   36]\n",
      " [ 309  132]]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "from sklearn.ensemble import RandomForestClassifier #随机森林分类器\n",
    "\n",
    "from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标\n",
    "from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵\n",
    "import warnings #用于忽略警告信息\n",
    "warnings.filterwarnings(\"ignore\") # 忽略所有警告信息\n",
    "# --- 1. 默认参数的随机森林 ---\n",
    "# 评估基准模型，这里确实不需要验证集\n",
    "print(\"--- 1. 默认参数随机森林 (训练集 -> 测试集) ---\")\n",
    "import time # 这里介绍一个新的库，time库，主要用于时间相关的操作，因为调参需要很长时间，记录下会帮助后人知道大概的时长\n",
    "start_time = time.time() # 记录开始时间\n",
    "rf_model = RandomForestClassifier(random_state=42)\n",
    "rf_model.fit(X_train, y_train) # 在训练集上训练\n",
    "rf_pred = rf_model.predict(X_test) # 在测试集上预测\n",
    "end_time = time.time() # 记录结束时间\n",
    "\n",
    "print(f\"训练与预测耗时: {end_time - start_time:.4f} 秒\")\n",
    "print(\"\\n默认随机森林 在测试集上的分类报告：\")\n",
    "print(classification_report(y_test, rf_pred))\n",
    "print(\"默认随机森林 在测试集上的混淆矩阵：\")\n",
    "print(confusion_matrix(y_test, rf_pred))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aeb6604c",
   "metadata": {},
   "source": [
    "核心思想： \n",
    "1. 这些启发式算法都是优化器。你的目标是找到一组超参数，让你的机器学习模型在某个指标（比如验证集准确率）上表现最好。\n",
    "2. 这个过程就像在一个复杂的地形（参数空间）上寻找最高峰（最佳性能）。\n",
    "3. 启发式算法就是一群聪明的“探险家”，它们用不同的策略（模仿自然、物理现象等）来寻找这个最高峰，而不需要知道地形每一处的精确梯度（导数）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc7c7523",
   "metadata": {},
   "source": [
    "## 遗传算法\n",
    "遗传算法 (Genetic Algorithm - GA)\n",
    "\n",
    "- 灵感来源： 生物进化，达尔文的“适者生存”。\n",
    "- 简单理解： 把不同的超参数组合想象成一群“个体”。表现好的个体（高验证分）更有机会“繁殖”（它们的参数组合会被借鉴和混合），并可能发生“变异”（参数随机小改动），产生下一代。表现差的个体逐渐被淘汰。一代代下去，种群整体就会越来越适应环境（找到更好的超参数）。\n",
    "- 应用感觉： 像是在大范围“撒网”搜索，通过优胜劣汰和随机变动逐步逼近最优解。适合参数空间很大、很复杂的情况。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "468b3b24",
   "metadata": {},
   "outputs": [],
   "source": [
    "# pip install deap -i https://pypi.tuna.tsinghua.edu.cn/simple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79d674c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- 2. 遗传算法优化随机森林 (训练集 -> 测试集) ---\n",
      "遗传算法优化耗时: 251.5941 秒\n",
      "最佳参数:  {'n_estimators': 158, 'max_depth': 25, 'min_samples_split': 10, 'min_samples_leaf': 1}\n",
      "\n",
      "遗传算法优化后的随机森林 在测试集上的分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.77      0.98      0.86      1059\n",
      "           1       0.83      0.28      0.42       441\n",
      "\n",
      "    accuracy                           0.77      1500\n",
      "   macro avg       0.80      0.63      0.64      1500\n",
      "weighted avg       0.79      0.77      0.73      1500\n",
      "\n",
      "遗传算法优化后的随机森林 在测试集上的混淆矩阵：\n",
      "[[1034   25]\n",
      " [ 316  125]]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "import time\n",
    "from deap import base, creator, tools, algorithms # DEAP是一个用于遗传算法和进化计算的Python库\n",
    "import random\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "\n",
    "# --- 2. 遗传算法优化随机森林 ---\n",
    "print(\"\\n--- 2. 遗传算法优化随机森林 (训练集 -> 测试集) ---\")\n",
    "\n",
    "# 定义适应度函数和个体类型\n",
    "creator.create(\"FitnessMax\", base.Fitness, weights=(1.0,))\n",
    "creator.create(\"Individual\", list, fitness=creator.FitnessMax)\n",
    "\n",
    "# 定义超参数范围\n",
    "n_estimators_range = (50, 200)\n",
    "max_depth_range = (10, 30)\n",
    "min_samples_split_range = (2, 10)\n",
    "min_samples_leaf_range = (1, 4)\n",
    "\n",
    "# 初始化工具盒\n",
    "toolbox = base.Toolbox()\n",
    "\n",
    "# 定义基因生成器\n",
    "toolbox.register(\"attr_n_estimators\", random.randint, *n_estimators_range)\n",
    "toolbox.register(\"attr_max_depth\", random.randint, *max_depth_range)\n",
    "toolbox.register(\"attr_min_samples_split\", random.randint, *min_samples_split_range)\n",
    "toolbox.register(\"attr_min_samples_leaf\", random.randint, *min_samples_leaf_range)\n",
    "\n",
    "# 定义个体生成器\n",
    "toolbox.register(\"individual\", tools.initCycle, creator.Individual,\n",
    "                 (toolbox.attr_n_estimators, toolbox.attr_max_depth,\n",
    "                  toolbox.attr_min_samples_split, toolbox.attr_min_samples_leaf), n=1)\n",
    "\n",
    "# 定义种群生成器\n",
    "toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)\n",
    "\n",
    "# 定义评估函数\n",
    "def evaluate(individual):\n",
    "    n_estimators, max_depth, min_samples_split, min_samples_leaf = individual\n",
    "    model = RandomForestClassifier(n_estimators=n_estimators,\n",
    "                                   max_depth=max_depth,\n",
    "                                   min_samples_split=min_samples_split,\n",
    "                                   min_samples_leaf=min_samples_leaf,\n",
    "                                   random_state=42)\n",
    "    model.fit(X_train, y_train)\n",
    "    y_pred = model.predict(X_test)\n",
    "    accuracy = accuracy_score(y_test, y_pred)\n",
    "    return accuracy,\n",
    "\n",
    "# 注册评估函数\n",
    "toolbox.register(\"evaluate\", evaluate)\n",
    "\n",
    "# 注册遗传操作\n",
    "toolbox.register(\"mate\", tools.cxTwoPoint)\n",
    "toolbox.register(\"mutate\", tools.mutUniformInt, low=[n_estimators_range[0], max_depth_range[0],\n",
    "                                                     min_samples_split_range[0], min_samples_leaf_range[0]],\n",
    "                 up=[n_estimators_range[1], max_depth_range[1],\n",
    "                     min_samples_split_range[1], min_samples_leaf_range[1]], indpb=0.1)\n",
    "toolbox.register(\"select\", tools.selTournament, tournsize=3)\n",
    "\n",
    "# 初始化种群\n",
    "pop = toolbox.population(n=20)\n",
    "\n",
    "# 遗传算法参数\n",
    "NGEN = 10\n",
    "CXPB = 0.5\n",
    "MUTPB = 0.2\n",
    "\n",
    "start_time = time.time()\n",
    "# 运行遗传算法\n",
    "for gen in range(NGEN):\n",
    "    offspring = algorithms.varAnd(pop, toolbox, cxpb=CXPB, mutpb=MUTPB)\n",
    "    fits = toolbox.map(toolbox.evaluate, offspring)\n",
    "    for fit, ind in zip(fits, offspring):\n",
    "        ind.fitness.values = fit\n",
    "    pop = toolbox.select(offspring, k=len(pop))\n",
    "\n",
    "end_time = time.time()\n",
    "\n",
    "# 找到最优个体\n",
    "best_ind = tools.selBest(pop, k=1)[0]\n",
    "best_n_estimators, best_max_depth, best_min_samples_split, best_min_samples_leaf = best_ind\n",
    "\n",
    "print(f\"遗传算法优化耗时: {end_time - start_time:.4f} 秒\")\n",
    "print(\"最佳参数: \", {\n",
    "    'n_estimators': best_n_estimators,\n",
    "    'max_depth': best_max_depth,\n",
    "    'min_samples_split': best_min_samples_split,\n",
    "    'min_samples_leaf': best_min_samples_leaf\n",
    "})\n",
    "\n",
    "# 使用最佳参数的模型进行预测\n",
    "best_model = RandomForestClassifier(n_estimators=best_n_estimators,\n",
    "                                    max_depth=best_max_depth,\n",
    "                                    min_samples_split=best_min_samples_split,\n",
    "                                    min_samples_leaf=best_min_samples_leaf,\n",
    "                                    random_state=42)\n",
    "best_model.fit(X_train, y_train)\n",
    "best_pred = best_model.predict(X_test)\n",
    "\n",
    "print(\"\\n遗传算法优化后的随机森林 在测试集上的分类报告：\")\n",
    "print(classification_report(y_test, best_pred))\n",
    "print(\"遗传算法优化后的随机森林 在测试集上的混淆矩阵：\")\n",
    "print(confusion_matrix(y_test, best_pred))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cf02d1c",
   "metadata": {},
   "source": [
    "上述代码看上去非常复杂，而且不具备复用性\n",
    "\n",
    "也就是说，即使你搞懂了这段代码，对你的提升也微乎其微，因为你无法对他进行改进（他永远是别人的东西），而且就算背熟悉了他，也对你学习其他的方法没什么帮助，即使你学完遗传算法学粒子群，也没有帮助。\n",
    "\n",
    "AI时代的工具很大的好处，就是找到了一个记忆工具来帮助我们记住这个方法需要的步骤，然后我们只需要调用这个工具，就可以完成这个任务。\n",
    "1. 关注输入和输出的格式和数据\n",
    "2. 关注方法的前生今世和各自的优势---优缺点和应用场景\n",
    "3. 关注模型本身的实现逻辑（如果用的时候很少，可跳过，借助ai实现）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "428a577c",
   "metadata": {},
   "source": [
    "## 粒子群方法\n",
    "粒子群优化 (Particle Swarm Optimization - PSO)\n",
    "\n",
    "- 灵感来源： 鸟群或鱼群觅食。\n",
    "- 简单理解： 把每个超参数组合想象成一个“粒子”（鸟）。每个粒子在参数空间中“飞行”。它会记住自己飞过的最好位置，也会参考整个“鸟群”发现的最好位置，结合这两者来调整自己的飞行方向和速度，同时带点随机性。\n",
    "- 应用感觉： 像是一群探险家，既有自己的探索记忆，也会互相交流信息（全局最佳位置），集体协作寻找目标。通常收敛比遗传算法快一些。\n",
    "\n",
    "粒子群方法的思想比较简单，所以甚至可以不调库自己实现。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99a761d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- 2. 粒子群优化算法优化随机森林 (训练集 -> 测试集) ---\n",
      "粒子群优化算法优化耗时: 374.1755 秒\n",
      "最佳参数:  {'n_estimators': 200, 'max_depth': 18, 'min_samples_split': 4, 'min_samples_leaf': 1}\n",
      "\n",
      "粒子群优化算法优化后的随机森林 在测试集上的分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.77      0.98      0.86      1059\n",
      "           1       0.83      0.29      0.43       441\n",
      "\n",
      "    accuracy                           0.77      1500\n",
      "   macro avg       0.80      0.63      0.64      1500\n",
      "weighted avg       0.79      0.77      0.73      1500\n",
      "\n",
      "粒子群优化算法优化后的随机森林 在测试集上的混淆矩阵：\n",
      "[[1034   25]\n",
      " [ 315  126]]\n"
     ]
    }
   ],
   "source": [
    "# --- 2. 粒子群优化算法优化随机森林 ---\n",
    "print(\"\\n--- 2. 粒子群优化算法优化随机森林 (训练集 -> 测试集) ---\")\n",
    "\n",
    "\n",
    "# 定义适应度函数，本质就是构建了一个函数实现 参数--> 评估指标的映射\n",
    "def fitness_function(params): \n",
    "    n_estimators, max_depth, min_samples_split, min_samples_leaf = params # 序列解包，允许你将一个可迭代对象（如列表、元组、字符串等）中的元素依次赋值给多个变量。\n",
    "    model = RandomForestClassifier(n_estimators=int(n_estimators),\n",
    "                                   max_depth=int(max_depth),\n",
    "                                   min_samples_split=int(min_samples_split),\n",
    "                                   min_samples_leaf=int(min_samples_leaf),\n",
    "                                   random_state=42)\n",
    "    model.fit(X_train, y_train)\n",
    "    y_pred = model.predict(X_test)\n",
    "    accuracy = accuracy_score(y_test, y_pred)\n",
    "    return accuracy\n",
    "\n",
    "\n",
    "# 粒子群优化算法实现\n",
    "def pso(num_particles, num_iterations, c1, c2, w, bounds): # 粒子群优化算法核心函数\n",
    "    # num_particles：粒子的数量，即算法中用于搜索最优解的个体数量。\n",
    "    # num_iterations：迭代次数，算法运行的最大循环次数。\n",
    "    # c1：认知学习因子，用于控制粒子向自身历史最佳位置移动的程度。\n",
    "    # c2：社会学习因子，用于控制粒子向全局最佳位置移动的程度。\n",
    "    # w：惯性权重，控制粒子的惯性，影响粒子在搜索空间中的移动速度和方向。\n",
    "    # bounds：超参数的取值范围，是一个包含多个元组的列表，每个元组表示一个超参数的最小值和最大值。\n",
    "\n",
    "    num_params = len(bounds) \n",
    "    particles = np.array([[random.uniform(bounds[i][0], bounds[i][1]) for i in range(num_params)] for _ in\n",
    "                          range(num_particles)])\n",
    "    velocities = np.array([[0] * num_params for _ in range(num_particles)])\n",
    "    personal_best = particles.copy()\n",
    "    personal_best_fitness = np.array([fitness_function(p) for p in particles])\n",
    "    global_best_index = np.argmax(personal_best_fitness)\n",
    "    global_best = personal_best[global_best_index]\n",
    "    global_best_fitness = personal_best_fitness[global_best_index]\n",
    "\n",
    "    for _ in range(num_iterations):\n",
    "        r1 = np.array([[random.random() for _ in range(num_params)] for _ in range(num_particles)])\n",
    "        r2 = np.array([[random.random() for _ in range(num_params)] for _ in range(num_particles)])\n",
    "\n",
    "        velocities = w * velocities + c1 * r1 * (personal_best - particles) + c2 * r2 * (\n",
    "                global_best - particles)\n",
    "        particles = particles + velocities\n",
    "\n",
    "        for i in range(num_particles):\n",
    "            for j in range(num_params):\n",
    "                if particles[i][j] < bounds[j][0]:\n",
    "                    particles[i][j] = bounds[j][0]\n",
    "                elif particles[i][j] > bounds[j][1]:\n",
    "                    particles[i][j] = bounds[j][1]\n",
    "\n",
    "        fitness_values = np.array([fitness_function(p) for p in particles])\n",
    "        improved_indices = fitness_values > personal_best_fitness\n",
    "        personal_best[improved_indices] = particles[improved_indices]\n",
    "        personal_best_fitness[improved_indices] = fitness_values[improved_indices]\n",
    "\n",
    "        current_best_index = np.argmax(personal_best_fitness)\n",
    "        if personal_best_fitness[current_best_index] > global_best_fitness:\n",
    "            global_best = personal_best[current_best_index]\n",
    "            global_best_fitness = personal_best_fitness[current_best_index]\n",
    "\n",
    "    return global_best, global_best_fitness\n",
    "\n",
    "\n",
    "# 超参数范围\n",
    "bounds = [(50, 200), (10, 30), (2, 10), (1, 4)]  # n_estimators, max_depth, min_samples_split, min_samples_leaf\n",
    "\n",
    "# 粒子群优化算法参数\n",
    "num_particles = 20\n",
    "num_iterations = 10\n",
    "c1 = 1.5\n",
    "c2 = 1.5\n",
    "w = 0.5\n",
    "\n",
    "start_time = time.time()\n",
    "best_params, best_fitness = pso(num_particles, num_iterations, c1, c2, w, bounds)\n",
    "end_time = time.time()\n",
    "\n",
    "print(f\"粒子群优化算法优化耗时: {end_time - start_time:.4f} 秒\")\n",
    "print(\"最佳参数: \", {\n",
    "    'n_estimators': int(best_params[0]),\n",
    "   'max_depth': int(best_params[1]),\n",
    "   'min_samples_split': int(best_params[2]),\n",
    "   'min_samples_leaf': int(best_params[3])\n",
    "})\n",
    "\n",
    "# 使用最佳参数的模型进行预测\n",
    "best_model = RandomForestClassifier(n_estimators=int(best_params[0]),\n",
    "                                    max_depth=int(best_params[1]),\n",
    "                                    min_samples_split=int(best_params[2]),\n",
    "                                    min_samples_leaf=int(best_params[3]),\n",
    "                                    random_state=42)\n",
    "best_model.fit(X_train, y_train)\n",
    "best_pred = best_model.predict(X_test)\n",
    "\n",
    "print(\"\\n粒子群优化算法优化后的随机森林 在测试集上的分类报告：\")\n",
    "print(classification_report(y_test, best_pred))\n",
    "print(\"粒子群优化算法优化后的随机森林 在测试集上的混淆矩阵：\")\n",
    "print(confusion_matrix(y_test, best_pred))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7c24db4",
   "metadata": {},
   "source": [
    "##  退火算法\n",
    "模拟退火 (Simulated Annealing - SA)\n",
    "\n",
    "- 灵感来源： 金属冶炼中的退火过程（缓慢冷却使金属达到最低能量稳定态）。\n",
    "- 简单理解： 从一个随机的超参数组合开始。随机尝试改变一点参数。如果新组合更好，就接受它。如果新组合更差，也有一定概率接受它（尤其是在“高温”/搜索早期）。这个接受坏解的概率会随着时间（“降温”）慢慢变小。\n",
    "- 应用感觉： 像一个有点“冲动”的探险家，初期愿意尝试一些看起来不太好的路径（为了跳出局部最优的小山谷），后期则越来越“保守”，专注于在当前找到的好区域附近精细搜索。擅长避免陷入局部最优。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84a01e9d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- 2. 模拟退火算法优化随机森林 (训练集 -> 测试集) ---\n",
      "模拟退火算法优化耗时: 129.1660 秒\n",
      "最佳参数:  {'n_estimators': 98, 'max_depth': 16, 'min_samples_split': 7, 'min_samples_leaf': 2}\n",
      "\n",
      "模拟退火算法优化后的随机森林 在测试集上的分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.77      0.98      0.86      1059\n",
      "           1       0.86      0.29      0.43       441\n",
      "\n",
      "    accuracy                           0.78      1500\n",
      "   macro avg       0.82      0.63      0.65      1500\n",
      "weighted avg       0.80      0.78      0.73      1500\n",
      "\n",
      "模拟退火算法优化后的随机森林 在测试集上的混淆矩阵：\n",
      "[[1039   20]\n",
      " [ 315  126]]\n"
     ]
    }
   ],
   "source": [
    "# --- 2. 模拟退火算法优化随机森林 ---\n",
    "print(\"\\n--- 2. 模拟退火算法优化随机森林 (训练集 -> 测试集) ---\")\n",
    "\n",
    "\n",
    "# 定义适应度函数\n",
    "def fitness_function(params): \n",
    "    n_estimators, max_depth, min_samples_split, min_samples_leaf = params\n",
    "    model = RandomForestClassifier(n_estimators=int(n_estimators),\n",
    "                                   max_depth=int(max_depth),\n",
    "                                   min_samples_split=int(min_samples_split),\n",
    "                                   min_samples_leaf=int(min_samples_leaf),\n",
    "                                   random_state=42)\n",
    "    model.fit(X_train, y_train)\n",
    "    y_pred = model.predict(X_test)\n",
    "    accuracy = accuracy_score(y_test, y_pred)\n",
    "    return accuracy\n",
    "\n",
    "\n",
    "# 模拟退火算法实现\n",
    "def simulated_annealing(initial_solution, bounds, initial_temp, final_temp, alpha):\n",
    "    current_solution = initial_solution\n",
    "    current_fitness = fitness_function(current_solution)\n",
    "    best_solution = current_solution\n",
    "    best_fitness = current_fitness\n",
    "    temp = initial_temp\n",
    "\n",
    "    while temp > final_temp:\n",
    "        # 生成邻域解\n",
    "        neighbor_solution = []\n",
    "        for i in range(len(current_solution)):\n",
    "            new_val = current_solution[i] + random.uniform(-1, 1) * (bounds[i][1] - bounds[i][0]) * 0.1\n",
    "            new_val = max(bounds[i][0], min(bounds[i][1], new_val))\n",
    "            neighbor_solution.append(new_val)\n",
    "\n",
    "        neighbor_fitness = fitness_function(neighbor_solution)\n",
    "        delta_fitness = neighbor_fitness - current_fitness\n",
    "\n",
    "        if delta_fitness > 0 or random.random() < np.exp(delta_fitness / temp):\n",
    "            current_solution = neighbor_solution\n",
    "            current_fitness = neighbor_fitness\n",
    "\n",
    "        if current_fitness > best_fitness:\n",
    "            best_solution = current_solution\n",
    "            best_fitness = current_fitness\n",
    "\n",
    "        temp *= alpha\n",
    "\n",
    "    return best_solution, best_fitness\n",
    "\n",
    "\n",
    "# 超参数范围\n",
    "bounds = [(50, 200), (10, 30), (2, 10), (1, 4)]  # n_estimators, max_depth, min_samples_split, min_samples_leaf\n",
    "\n",
    "# 模拟退火算法参数\n",
    "initial_temp = 100 # 初始温度\n",
    "final_temp = 0.1 # 终止温度\n",
    "alpha = 0.95 # 温度衰减系数\n",
    "\n",
    "# 初始化初始解\n",
    "initial_solution = [random.uniform(bounds[i][0], bounds[i][1]) for i in range(len(bounds))]\n",
    "\n",
    "start_time = time.time()\n",
    "best_params, best_fitness = simulated_annealing(initial_solution, bounds, initial_temp, final_temp, alpha)\n",
    "end_time = time.time()\n",
    "\n",
    "print(f\"模拟退火算法优化耗时: {end_time - start_time:.4f} 秒\")\n",
    "print(\"最佳参数: \", {\n",
    "    'n_estimators': int(best_params[0]),\n",
    "    'max_depth': int(best_params[1]),\n",
    "    'min_samples_split': int(best_params[2]),\n",
    "    'min_samples_leaf': int(best_params[3])\n",
    "})\n",
    "\n",
    "# 使用最佳参数的模型进行预测\n",
    "best_model = RandomForestClassifier(n_estimators=int(best_params[0]),\n",
    "                                    max_depth=int(best_params[1]),\n",
    "                                    min_samples_split=int(best_params[2]),\n",
    "                                    min_samples_leaf=int(best_params[3]),\n",
    "                                    random_state=42)\n",
    "best_model.fit(X_train, y_train)\n",
    "best_pred = best_model.predict(X_test)\n",
    "\n",
    "print(\"\\n模拟退火算法优化后的随机森林 在测试集上的分类报告：\")\n",
    "print(classification_report(y_test, best_pred))\n",
    "print(\"模拟退火算法优化后的随机森林 在测试集上的混淆矩阵：\")\n",
    "print(confusion_matrix(y_test, best_pred))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "DL",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
